/** * GET /api/products/[id] * * Returns a single product by UUID. * Returns 404 if product is not found, is inactive, or user's role doesn't have access. * * Role-based Visibility (MVP): * - Unauthenticated users: 404 * - Authenticated users: Only see products assigned to their ACTIVE role */ import { z } from 'zod' import { and, eq } from 'drizzle-orm' import { products } from '../../database/schema' import { getVisibleProductIdsForRole } from '../../utils/roles' import { getUserActiveRole } from '../../utils/role-session' // UUID validation schema const paramsSchema = z.object({ id: z.string().uuid('Invalid product ID format'), }) export default defineEventHandler(async (event) => { const db = useDatabase() // Validate and extract product ID from route params const params = await getValidatedRouterParams(event, paramsSchema.parse) try { // Get user session (MVP: unauthenticated users cannot access products) const { user } = await getUserSession(event) if (!user) { throw createError({ statusCode: 404, statusMessage: 'Product not found', }) } // Get user's active role const activeRole = await getUserActiveRole(event) // Check role-based visibility const visibleProductIds = await getVisibleProductIdsForRole(user.id, activeRole) // Return 404 if product is not visible to user's role if (!visibleProductIds.includes(params.id)) { throw createError({ statusCode: 404, statusMessage: 'Product not found', }) } // Fetch product by ID (must be active) const product = await db.query.products.findFirst({ where: and(eq(products.id, params.id), eq(products.active, true)), }) // Return 404 if product not found or inactive if (!product) { throw createError({ statusCode: 404, statusMessage: 'Product not found', }) } return product } catch (error) { // Re-throw createError errors as-is if (error && typeof error === 'object' && 'statusCode' in error) { throw error } console.error('Error fetching product:', error) throw createError({ statusCode: 500, statusMessage: 'Failed to fetch product', }) } })